﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Collections;
using System.Net;

using Microsoft.Web.Administration;

using King.Extensions;
using King.Reflection;
using King.Text;
using King.Automation;
using King.Declarative;

namespace King.InternetInformationService {

    /// <summary>
    /// Commands to manipulate sties.
    /// </summary>
    [CommandGroup]
    public abstract class SiteCommand : IisCommand {

        internal SiteCommand(params object[] arguments)
            : base(arguments) {
        }
    }
    public sealed class SiteDefault : SiteCommand {

        [Command(IsDefault = true)]
        internal SiteDefault() {
        }

        private IEnumerable Dump(IPEndPoint endPoint) {
            object address = endPoint.Address;
            if (endPoint.Address.Address == 0)
                address = "*";

            return new TextJoin(":") { address, endPoint.Port };
        }
        private IEnumerable Dump(Site site) {
            return new TextJoin(" ") {
                new ConsoleWhite { site.Name },
                site.Id,
                site.State == ObjectState.Started ? (object)new ConsoleGreen { "Started" } :
                    site.State == ObjectState.Starting ? (object)new ConsoleYellow { "Starting" } :
                    site.State == ObjectState.Stopping ? (object)new ConsoleYellow { "Stopping" } :
                    site.State == ObjectState.Stopped ? (object)new ConsoleRed { "Stopped" } : "Unknown",
                new TextJoin(",", "[", "]") { 
                    from o in site.Bindings
                    select new TextJoin(" ") {
                        Dump(o.EndPoint), 
                        new TextJoin(prefix: "(", suffix: ")") { 
                            o.Protocol 
                        }
                    }
                },
                site.Applications[0].VirtualDirectories[0].PhysicalPath,
            };
        }

        private IEnumerable Execute(IisContext context) {

            return new TextSingleSpaced {
                from o in context.ServerManager.Sites
                orderby o.Name
                select Dump(o)
            };
        }
    }

    public abstract class SiteVerb : SiteCommand {

        private string m_verb;

        internal SiteVerb(string verb, params object[] parameters)
            : base(parameters) {

            m_verb = verb;
        }

        private IEnumerable ActionHelper(Site site) {
            yield return new TextJoin(" ") {
                new ConsoleFlush {
                    new TextJoin(" ") {
                        m_verb, 
                        new TextJoin { site.Name, "..." }
                    }
                },

                Action(site)
            };
        }
        protected abstract IEnumerable Action(Site appPool);

        internal IEnumerable Execute(IisContext context, string name) {

            return new TextSingleSpaced {
                from o in Sites(context.ServerManager, name)
                select new SubstantialDeclaration { ActionHelper(o) }
            };
        }
    }
    public sealed class SiteStop : SiteVerb {

        [Command]
        internal SiteStop(
            [CommandTarget]string name = null)
            : base("Stopping", name) {
        }

        protected override IEnumerable Action(Site site) {
            if (site.State == ObjectState.Stopped)
                yield return new ConsoleYellow { "the application pool is already stopped." };
            else {
                site.Stop();
                yield return new ConsoleRed { "stopped." };
            }
        }
    }
    public sealed class SiteStart : SiteVerb {

        [Command]
        internal SiteStart(
            [CommandTarget]string name = null)
            : base("Starting", name) {
        }

        protected override IEnumerable Action(Site site) {
            if (site.State == ObjectState.Started) {
                yield return new ConsoleYellow { "the application pool is already started." };
            } else {
                site.Start();
                yield return new ConsoleGreen { "started." };
            }
        }
    }
    public sealed class SiteRecycle : SiteVerb {

        [Command]
        internal SiteRecycle(
            [CommandTarget]string name = null)
            : base("Recycling", name) {
        }

        protected override IEnumerable Action(Site site) {
            if (site.State == ObjectState.Stopped) {
                yield return new ConsoleYellow { "the application pool is stopped. Restarting..." };
                site.Start();
                yield return new ConsoleGreen { "started." };
            } else {
                site.Stop();
                site.Start();
                yield return new ConsoleGreen { "recycled." };
            }
        }
    }

    public sealed class SiteRemove : SiteCommand {

        [Command]
        public SiteRemove(
            string name)
            : base(name) {
        }

        internal IEnumerable Execute(IisContext context, string name) {

            var site = GetSite(context.ServerManager, name);

            // create site at port and path
            context.ServerManager.Sites.Remove(site);
            
            // save changes
            context.ServerManager.CommitChanges();

            // report
            yield return "Site removed.";
        }
    }
    public sealed class SiteAdd : SiteCommand {

        [Command]
        public SiteAdd(
            string name, 
            [CommandTarget(Path = CommandPathInfo.IsDirectory)]string path, 
            int port,
            string appPool)
            : base(name, path, port, appPool) {
        }

        internal IEnumerable Execute(IisContext context, string name, string path, int port, string appPool) {
            // attempt to resolve application pool
            GetApplicationPool(context.ServerManager, appPool);

            // create site at port and path
            var site = context.ServerManager.Sites.Add(name, path, port);
            site.Applications[0].ApplicationPoolName = appPool;

            // save changes
            context.ServerManager.CommitChanges();

            // report
            yield return "Site added.";
        }
    }
}
